Skip to content

feat: JIT-friendly Einstein radius helper + AnalysisDataset jit latent mode - #435

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/euclid-einstein-radius-zero-contour
May 21, 2026
Merged

feat: JIT-friendly Einstein radius helper + AnalysisDataset jit latent mode#435
Jammy2211 merged 1 commit into
mainfrom
feature/euclid-einstein-radius-zero-contour

Conversation

@Jammy2211

@Jammy2211 Jammy2211 commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two coupled changes that unlock JAX-mode latent computation for lensing pipelines:

  1. LensCalc.einstein_radius_jit_from(init_guess, ...) — new ~95-line helper, fully JIT-friendly. Bypasses the non-jit-able pieces of the existing einstein_radius_via_zero_contour_from: no marching-squares seed search (_init_guess_from_coarse_grid uses skimage and breaks JAX trace), no ZeroSolver.path_reduce (upstream documents it as un-jit-able), no float() cast. Computes the enclosed area on the raw NaN-padded paths array via JAX-vectorised shoelace with jnp.where(jnp.isfinite, ...) masking, takes max area across seeds (robust to duplicate seeds on the same curve), returns a scalar jax.Array. Reuses the (f, ZeroSolver) cache from perf(lens_calc): cache (f, ZeroSolver) for zero_contour critical curves #434.

  2. AnalysisDataset.LATENT_BATCH_MODE = "jit" — overrides PyAutoFit's default "vmap". Required because both the new helper and the underlying ZeroSolver use jax.lax.cond / jax.lax.while_loop for early termination, which upstream explicitly documents as vmap-incompatible. All PyAutoGalaxy/PyAutoLens analysis classes inherit this override automatically.

Together these make compute_latent_samples with use_jax=True work for lensing latents — previously impossible because the only Einstein-radius computation available was einstein_radius_via_zero_contour_from (Python loops + variable-length lists, not jit-able) or einstein_radius_from(grid=...) (marching squares + not JAX-traceable).

API Changes

Two additions to PyAutoGalaxy: a new public method LensCalc.einstein_radius_jit_from, and a class attribute override AnalysisDataset.LATENT_BATCH_MODE = "jit". No existing API surface is modified or removed.

See full details below.

Test Plan

  • New unit test: LensCalc.einstein_radius_jit_from exists with required init_guess argument and the expected kwargs (delta, N, pixel_scales, tol, max_newton). JAX-runtime behaviour exercised in the workspace integration (no JAX in library unit tests per project policy).
  • New unit test: AnalysisDataset.LATENT_BATCH_MODE == "jit".
  • pytest test_autogalaxy/operate/ test_autogalaxy/analysis/ — 78 passed locally.
  • End-to-end on the Euclid pipeline workspace (euclid_strong_lens_modeling_pipeline start_here.py under PYAUTO_TEST_MODE=1): latent.effective_einstein_radius = 2.1002 arcsec on the prior-median MGE tracer. ~480 ms/sample on CPU after the ~10 s first-sample compile.
  • Pipeline numpy-branch smoke (PYAUTO_DISABLE_JAX=1): 6/6 PASS — no regression on the legacy path.
Full API Changes (for automation & release notes)

Added

  • LensCalc.einstein_radius_jit_from(init_guess, delta=0.05, N=500, pixel_scales=(0.05, 0.05), tol=1e-6, max_newton=5) -> jax.Array — JIT-friendly Einstein radius via zero-contour. Takes a static init_guess of shape (n_seeds, 2), returns a scalar jax.Array. Designed for use inside jax.jit(...) per-sample; do NOT combine with jax.vmap (upstream jax_zero_contour.ZeroSolver uses lax.cond / lax.while_loop not safe under vmap).
  • AnalysisDataset.LATENT_BATCH_MODE: str = "jit" — class attribute override of PyAutoFit's "vmap" default. Inherited by AnalysisImaging, AnalysisInterferometer, AnalysisEllipse, AnalysisQuantity, and downstream PyAutoLens subclasses.

Changed Signature

  • None.

Changed Behaviour

  • For PyAutoGalaxy/PyAutoLens analyses, compute_latent_samples with use_jax=True now dispatches via per-sample jax.jit instead of jax.jit(jax.vmap(...)). JIT compile cache is reused across samples; speed is faster than per-sample NumPy but slower than the (vmap-incompatible) batched ideal. Required to unlock JAX-mode lensing latents at all.

Migration

  • None required. Existing callers of einstein_radius_via_zero_contour_from are unchanged. New einstein_radius_jit_from is an additive opt-in for JIT contexts.

Companion PRs

  • PyAutoFit: https://github.com/rhayes777/PyAutoFit/pull/1288 — adds the LATENT_BATCH_MODE attribute machinery this override depends on.
  • Workspace: https://github.com/PyAutoLabs/euclid_strong_lens_modeling_pipeline/pull/15 — wires the Euclid effective_einstein_radius latent through einstein_radius_jit_from.

Closes PyAutoLabs/euclid_strong_lens_modeling_pipeline#14 alongside the companion PRs.

🤖 Generated with Claude Code

…t mode

Two coupled changes that unlock JAX-mode latent computation for
lensing pipelines (Euclid, clusters, anywhere `compute_latent_samples`
runs with `use_jax=True`):

1. LensCalc.einstein_radius_jit_from(init_guess, ...) — new ~95-line
   helper that bypasses the non-jit-able pieces of the existing
   `einstein_radius_via_zero_contour_from`:
   - Requires a static `init_guess` (caller provides a small fan of
     JAX-array seeds) — skips the marching-squares seed search in
     `_init_guess_from_coarse_grid` which uses skimage and is not
     JAX-traceable.
   - Skips `ZeroSolver.path_reduce`, which is explicitly documented
     as un-jit-able (variable-length output).
   - Computes the enclosed area on the raw NaN-padded paths array
     via JAX-vectorised shoelace with `jnp.where(jnp.isfinite, ...)`
     masking. Takes max area across seeds (robust to duplicate seeds
     landing on the same curve).
   - Returns a scalar `jax.Array` directly (no `float()` cast).
   - Reuses the `(f, ZeroSolver)` cache from PR #434 so the warm
     call inside a jit-cached `compute_latent_variables` hits the
     JAX compile cache.

2. AnalysisDataset.LATENT_BATCH_MODE = "jit" — overrides PyAutoFit's
   default "vmap". Required because the new helper (and
   ZeroSolver in general) is not vmap-safe: upstream
   `jax_zero_contour.zero_contour_finder` uses `jax.lax.cond` and
   `jax.lax.while_loop` for early termination which combine poorly
   with vmap. AnalysisImaging, AnalysisInterferometer, and the
   PyAutoLens variants inherit this override automatically.

Companion PR: rhayes777/PyAutoFit#<TBD> adds the LATENT_BATCH_MODE
attribute that this override targets. Workspace PR:
PyAutoLabs/euclid_strong_lens_modeling_pipeline#14 wires the
Euclid effective_einstein_radius latent through the new helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label May 21, 2026
@Jammy2211
Jammy2211 merged commit 2547ca1 into main May 21, 2026
6 checks passed
@Jammy2211
Jammy2211 deleted the feature/euclid-einstein-radius-zero-contour branch May 21, 2026 19:53
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(latents): re-enable effective_einstein_radius via zero_contour

1 participant